package com.zonta.finance.serivce.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.zonta.finance.common.exception.BizException;
import com.zonta.finance.common.service.TokenService;
import com.zonta.finance.common.utils.DateUtil;
import com.zonta.finance.config.ObjectConvertor;
import com.zonta.finance.constant.RedisKeyConstant;
import com.zonta.finance.domain.dto.AdminDTO;
import com.zonta.finance.domain.form.CreateMenuForm;
import com.zonta.finance.domain.form.DelMenuForm;
import com.zonta.finance.domain.form.UpdateMenuForm;
import com.zonta.finance.domain.po.SysMenu;
import com.zonta.finance.domain.vo.GetMenuByIdVo;
import com.zonta.finance.domain.vo.ListTreeMenuVo;
import com.zonta.finance.domain.vo.ListTreeSelectMenuVo;
import com.zonta.finance.mapper.SysMenuMapper;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zonta.finance.serivce.ISysMenuService;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统菜单 服务实现类
 * </p>
 *
 * @author zonta
 * @since 2024-07-24
 */
@Service
@RequiredArgsConstructor
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {
    final ObjectConvertor objectConvertor;
    final RedissonClient redissonClient;
    final TokenService<AdminDTO> tokenService;
    /**
     * 获取树形菜单
     * @param
     * @return
     */
    @Override
    public List<ListTreeMenuVo> listTreeMenu(String name) {
        if (Strings.isNotBlank(name)) {
            List<SysMenu> sysMenus = getlist(name);
            return objectConvertor.toListTreeMenuVo(sysMenus);
        } else {
            return listTreeMenuByMenuIds();
        }
    }

    @Override
    public List<ListTreeMenuVo> listTreeMenuByMenuIds() {
        List<ListTreeMenuVo> result = null;
        List<SysMenu> list = list();
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        List<SysMenu> parentMenuList = list.stream().filter(p -> p.getPid() == 0)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(parentMenuList)) {
            return null;
        }
        result = objectConvertor.toListTreeMenuVo(parentMenuList);
        for (ListTreeMenuVo listTreeMenuVo : result) {
            List<SysMenu> childMenuList = list.stream().filter(p -> Objects.equals(p.getPid(), listTreeMenuVo.getId()))
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(childMenuList)) {
                continue;
            }
            listTreeMenuVo.setChildren(objectConvertor.toListTreeMenuVo(childMenuList));
        }
        return result;
    }

    @Override
    public Boolean del(DelMenuForm form) {
        RLock rLock = redissonClient.getLock(RedisKeyConstant.CHANGE_MENU_LOCK);
        try {
            rLock.lock();
            if (countByPid(form.getId()) > 0) {
                throw new BizException("该菜单下有子菜单不能直接删除");
            }
            boolean update = update(new LambdaUpdateWrapper<SysMenu>()
                    .set(SysMenu::getUpdateTime, DateUtil.getSystemTime())
                    .set(SysMenu::getUpdateMemberId, tokenService.getThreadLocalUserId())
                    .set(SysMenu::getDelFlag,true)
                    .eq(SysMenu::getId,form.getId()));

            return update;
        } catch (Exception ex) {
            throw new BizException(ex.getMessage(), ex);
        } finally {
            rLock.unlock();
        }
    }

    @Override
    public List<ListTreeSelectMenuVo> listTreeSelectMenu() {
        List<ListTreeSelectMenuVo> result = new ArrayList<>();
        ListTreeSelectMenuVo rootMenu = new ListTreeSelectMenuVo();
        rootMenu.setId(0);
        rootMenu.setValue("0");
        rootMenu.setTitle("根菜单");
        result.add(rootMenu);
        List<SysMenu> list = getlist();
        if (CollectionUtils.isEmpty(list)) {
            return result;
        }

        List<SysMenu> parentMenuList = list.stream().filter(p -> p.getPid() == 0)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(parentMenuList)) {
            return result;
        }
        rootMenu.setChildren(objectConvertor.toListTreeSelectMenuVo(parentMenuList));
        for (ListTreeSelectMenuVo listTreeSelectMenuVo : rootMenu.getChildren()) {
            List<SysMenu> childMenuList = list.stream().filter(p -> Objects.equals(p.getPid(), listTreeSelectMenuVo.getId()))
                    .collect(Collectors.toList());
            listTreeSelectMenuVo.setChildren(objectConvertor.toListTreeSelectMenuVo(childMenuList));
        }
        return result;
    }
    @Override
    public List<SysMenu> getlist(){
        LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysMenu::getDisable,false);
        wrapper.orderByAsc(SysMenu::getSort);
        return list(wrapper);
    }
    public List<SysMenu> getlist(String name){
        LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysMenu::getDisable,false);
        wrapper.eq(SysMenu::getName,name);
        wrapper.orderByAsc(SysMenu::getSort);
        return list(wrapper);
    }
    /**
     * 统计父结点下有多少子节点
     * @param pid
     * @return
     */
    @Override
    public int countByPid(int pid) {
        return count(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getPid,pid).eq(SysMenu::getDisable,false));
    }
    public int checkParent(int pid){
        if (pid==0){
            return 0;
        }
        SysMenu sysMenu = getById(pid);
        if (ObjUtil.isEmpty(sysMenu)){
            throw new BizException("父级菜单不存在");
        }
        if (sysMenu.getPid() > 0) {
            throw new BizException("不可以创建三级菜单");
        }
        return sysMenu.getNodePath();
    }
    @Override
    public Boolean create(CreateMenuForm form) {
        RLock rLock = redissonClient.getLock(RedisKeyConstant.CHANGE_MENU_LOCK);
        try {
            rLock.lock();
            //检测父级菜单
            int parentLevel = checkParent(form.getPid());
            //更新菜单顺序
            updateSort(form.getPid(), form.getSort());
            SysMenu sysMenu = objectConvertor.toSysMenu(form);
            sysMenu.initDefault();
            sysMenu.setNodePath(parentLevel + 1);
            sysMenu.setMemberId(tokenService.getThreadLocalUserId());
            sysMenu.setUpdateMemberId(tokenService.getThreadLocalUserId());
            baseMapper.insert(sysMenu);
            return true;
        } catch (Exception ex) {
            throw new BizException(ex.getMessage(), ex);
        } finally {
            rLock.unlock();
        }
    }

    @Override
    public GetMenuByIdVo getMenuById(Integer id) {
        SysMenu sysMenu = getById(id);
        return objectConvertor.toGetMenuByIdVo(sysMenu);
    }

    @Override
    public Boolean updateByForm(UpdateMenuForm form) {
        SysMenu sysMenu = getById(form.getId());
        if (sysMenu == null) {
            throw new BizException("菜单不存在");
        }
        RLock rLock = redissonClient.getLock(RedisKeyConstant.CHANGE_MENU_LOCK);
        try {
            rLock.lock();
            //更新菜单顺序
            updateSort(sysMenu.getPid(), form.getSort());
            SysMenu menu = BeanUtil.copyProperties(form, SysMenu.class);
            return baseMapper.updateById(menu) > 0;
        } catch (Exception ex) {
            throw new BizException(ex.getMessage(), ex);
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 更新菜单顺序
     *
     * @param pid
     * @param sort
     * @return
     */
    private boolean updateSort(int pid, int sort) {
        boolean flag = update(new LambdaUpdateWrapper<SysMenu>().eq(SysMenu::getPid, pid).eq(SysMenu::getDisable, false).set(SysMenu::getSort, 1));
        return flag;
    }
}
